﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.Linq;
using Snowflake.Entity;

namespace Snowflake.Gateway
{
    internal class QueryTranslator<TEntity> where TEntity : EntityObject<TEntity>, new()
    {
        private MethodCallExpression _Expression;
        private DataContext _DataContext;

        public QueryTranslator(DataContext dataContext, MethodCallExpression expression)
        {
            this._DataContext = dataContext;
            this._Expression = expression;


        }

        public string TableName
        {
            get
            {
                string[] words = this._DataContext.Mapping.GetTable(typeof(TEntity)).TableName.Split('.');

                string[] formattedWords = (from item in words
                                           select "[" + item + "]").ToArray();

                string tableName = String.Join(".", formattedWords);
                return tableName;
            }
        }

        public IQueryable<TEntity> Translate()
        {
            Table<TEntity> table = null;

            MethodCallExpression resultExression = this.DoTranslation(this._Expression, ref table);

            if (table == null)
                throw new InvalidOperationException("The query is not data base invokation query.");

            IQueryProvider provider = table as IQueryProvider;
            IQueryable<TEntity> newQuery = provider.CreateQuery<TEntity>(resultExression);
            return newQuery;
        }

        private MethodCallExpression DoTranslation(MethodCallExpression expression, ref Table<TEntity> table)
        {
            List<Expression> arguments = new List<Expression>();

            foreach (Expression arg in expression.Arguments)
            {
                Expression currentArgument = arg;

                if (arg is MethodCallExpression)
                    currentArgument = this.DoTranslation(currentArgument as MethodCallExpression, ref table);
                else if (arg is ConstantExpression)
                {
                    if (arg.Type.GetInterface("ITable") != null)
                    {
                        Type genericType = arg.Type.GetGenericArguments()[0];
                        ITable currentTable = this._DataContext.GetTable(genericType);

                        if (currentTable is Table<TEntity>)
                            table = currentTable as Table<TEntity>;

                        currentArgument = Expression.Constant(currentTable);
                    }
                }

                arguments.Add(currentArgument);
            }

            MethodCallExpression translatedExpression = Expression.Call(expression.Method, arguments.ToArray());
            return translatedExpression;
        }

    }
}
